package controllers

import java.io.File
import command.{CommandUtils, MissionUtils}
import dao._

import javax.inject.Inject
import play.api.mvc._
import tool.{FileTool, FormTool, Tool}

import scala.concurrent.ExecutionContext.Implicits.global
import implicits.Implicits._
import models.Tables._
import org.joda.time.DateTime
import shared.VarTool
import utils.Utils

import scala.concurrent.Future
import implicits.Implicits._
import org.zeroturnaround.zip.ZipUtil
import play.api.i18n.I18nSupport
import shared.Pojo._
import tool.Pojo.MyFileData
import tool.soft._

import scala.collection.immutable.SeqMap
import upickle.default._


/**
 * Created by yz on 23/7/2020
 */
class DataController @Inject()(cc: ControllerComponents)(
  implicit val modeDao: ModeDao,
  implicit val fileDataDao: FileDataDao,
  implicit val missionDao: MissionDao,
  implicit val groupFileDataDao: GroupFileDataDao,
) extends AbstractController(cc) with I18nSupport {

  def manageBefore = Action { implicit request =>
    Ok(views.html.user.data.manage())
  }

  def downloadExampleData = Action { implicit request =>
    val data = FormTool.fileNameForm.bindFromRequest().get
    val exampleDir = Tool.exampleDir
    val file = new File(exampleDir, data.fileName)
    Ok.sendFile(file).withHeaders(
      CONTENT_DISPOSITION -> s"attachment; filename=${
        file.getName
      }",
      CONTENT_TYPE -> "application/x-download"
    )
  }

  def getFileContent = Action { implicit request =>
    val data = FormTool.fileNameForm.bindFromRequest().get
    val file = new File(Tool.exampleDir, data.fileName)
    val str = file.str
    Ok(str)
  }

  def getAllFileData = Action.async { implicit request =>
    val userId = Tool.getUserId
    val data = FormTool.kindOpForm.bindFromRequest().get
    val kind = data.kind.map(x => x).getOrElse(VarTool.metDataStr)
    val f = fileDataDao.selectAll(userId, kind)
    f.map { x =>
      Future {
        val fileNames = x.map(x => s"${x.fileName}")
        val kindDir = Tool.getUserDataKindDir(userId, kind)
        kindDir.listFiles().filter {
          file =>
            !fileNames.contains(file.getName)
        }.foreach(_.deleteQuietly)
      }
      val array = Utils.getArrayByTs(x)
      Ok(writeJs(array))
    }
  }

  def getAllExampleData = Action { implicit request =>
    val softKind = FormTool.softKindForm.bindFromRequest().get.softKind
    val userId = Tool.getUserId
    val dir = new File(Tool.softExampleDir, softKind)
    val fileName = FormTool.fileNameForm.bindFromRequest().get.fileName
    val rows = fileName.mySplit(";").map { fileName =>
      val file = new File(dir, fileName)
      MyFileData(file.getName, "other", "", file.size)
    }
    val array = Utils.getArrayByTs(rows)
    Ok(writeJs(array))
  }

  def deleteFileDataById = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      val data = FormTool.idForm.bindFromRequest().get
      fileDataDao.select(data.id).flatMap { row =>
        Future {
          val file = Tool.getUserDataKindFile(row)
          file.deleteQuietly
        }
        fileDataDao.delete(userId, data.id).map { x =>
          Ok(writeJs("success"))
        }
      }
  }

  def deletes = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      val data = FormTool.idsForm.bindFromRequest().get
      fileDataDao.selectAll(data.ids).flatMap { rows =>
        Future {
          rows.foreach { row =>
            val file = Tool.getUserDataKindFile(row)
            file.deleteQuietly
          }
        }
        fileDataDao.deletes(userId, data.ids).map { x =>
          Ok(writeJs("success"))
        }
      }
  }

  def fileNameNonExistCheck = Action.async {
    implicit request =>
      val data = FormTool.fileNameForm.bindFromRequest.get
      val kind = FormTool.kindForm.bindFromRequest.get.kind
      val originalFileNameOp = FormTool.originalFileNameOpForm.bindFromRequest().get.originalFileName
      val userId = Tool.getUserId
      val fileName = data.fileName.addSuffix
      fileDataDao.selectAllFileName(userId, kind).map { fileNames =>
        val valid = originalFileNameOp.map { originalFileName =>
          if (originalFileName == data.fileName) {
            true
          } else {
            !fileNames.contains(fileName)
          }
        }.getOrElse {
          !fileNames.contains(fileName)
        }
        Ok(ujson.Obj("valid" -> valid))
      }
  }

  def update = Action.async { implicit request =>
    val id = FormTool.idForm.bindFromRequest().get.id
    val data = FormTool.updateForm.bindFromRequest().get
    fileDataDao.select(id).flatMap { row =>
      val newRow = row.copy(fileName = data.fileName.addSuffix, tags = data.tags, comment = data.comment)
      val kindDir = Tool.getUserDataKindDir(row)
      val newFile = new File(kindDir, s"${data.fileName.addSuffix}")
      new File(kindDir, row.fileName).renameTo(newFile)
      fileDataDao.update(newRow).map { x =>
        Ok(writeJs("success"))
      }
    }
  }

  def getFileData = Action.async { implicit request =>
    val softKindOp = FormTool.softKindOpForm.bindFromRequest().get.softKind
    val fileF = softKindOp.map { softKind =>
      Future {
        val fileName = FormTool.fileNameForm.bindFromRequest().get.fileName
        val dir = new File(Tool.softExampleDir, softKind)
        new File(dir, fileName)
      }
    }.getOrElse {
      val data = FormTool.idForm.bindFromRequest().get
      fileDataDao.select(data.id).map { row =>
        Tool.getUserDataKindFile(row)
      }
    }
    fileF.map { file =>
      val (columnNames, array) = Utils.getInfoByLines(file.txtLines)
      val json = ujson.Obj("columnNames" -> columnNames, "array" -> array)
      Ok(json)
    }
  }

  def getFileDataByFileName = Action.async { implicit request =>
    val userId = Tool.getUserId
    val fileName = FormTool.fileNameForm.bindFromRequest().get.fileName
    val fileF = Tool.produceRawDataFileF(userId, fileName)
    fileF.map { file =>
      val (columnNames, array) = Utils.getInfoByLines(file.txtLines)
      val json = ujson.Obj("columnNames" -> columnNames, "array" -> array)
      Ok(json)
    }
  }

  def getFileDataById = Action.async {
    implicit request =>
      val data = FormTool.idForm.bindFromRequest().get
      fileDataDao.select(data.id).map {
        x =>
          val json = writeJs(x)
          Ok(json)
      }
  }

  def getExampleData = Action {
    implicit request =>
      val softKind = FormTool.softKindForm.bindFromRequest().get.softKind
      val fileName = FormTool.fileNameForm.bindFromRequest().get.fileName
      val kindDir = new File(Tool.softExampleDir, softKind)
      val file = new File(kindDir, fileName)
      val row = Tool.getMyFileData(file)
      val json = writeJs(row)
      Ok(json)
  }

  def getRowCols = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      val data = FormTool.fileNameForm.bindFromRequest().get
      Tool.produceRawDataLinesF(userId, data.fileName).map { lines =>
        val columnNames = lines.head.drop(1)
        val rowNames = lines.map(_.head).drop(1)
        Ok(ujson.Obj("headers" -> columnNames, "rowNames" -> rowNames))
      }
  }

  def getNumericColNames = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      val data = FormTool.fileNameForm.bindFromRequest().get
      Tool.produceRawDataLinesF(userId, data.fileName).map { lines =>
        val columnNames = lines.head.drop(1)
        val filterColumnNames = columnNames.filter { columnName =>
          val columns = lines.selectOneColumn(columnName)
          columns.forall(_.isDouble)
        }
        Ok(ujson.Obj("headers" -> filterColumnNames))
      }
  }

  def downloadFileById = Action.async {
    implicit request =>
      val data = FormTool.idForm.bindFromRequest().get
      fileDataDao.select(data.id).map {
        row =>
          val file = Tool.getUserDataKindFile(row)
          Ok.sendFile(file).withHeaders(
            CONTENT_DISPOSITION -> Tool.getContentDisposition(s"${
              file.getName
            }"),
            CONTENT_TYPE -> "application/x-download"
          )
      }
  }

  def detailBefore = Action.async {
    implicit request =>
      val idData = FormTool.idOpForm.bindFromRequest().get
      val userId = Tool.getUserId
      idData.id.map {
        x =>
          Future {
            Ok(views.html.user.data.detail(x))
          }
      }.getOrElse {
        val data = FormTool.fileNameForm.bindFromRequest().get
        val fileName = data.fileName
        val names = fileName.split("/")
        val isMission = names(0) == "mission"
        if (!isMission) {
          if (names(0) == "example") {
            Future {
              Ok(views.html.user.data.exampleDetail(names(1), names(2)))
            }
          } else {
            val kind = VarTool.showDirNameMap(names(0)).name
            fileDataDao.select(userId, names(1), kind).map(_.id).map {
              x =>
                Ok(views.html.user.data.detail(x))
            }
          }
        } else {
          val missionName = names(1)
          missionDao.select(userId, missionName).map {
            x =>
              Redirect(s"${
                routes.MissionController.viewCsvFileContent
              }?missionId=${
                x.id
              }&fileName=${names(2)}")
          }
        }
      }
  }

  def newGroup = Action.async {
    implicit request =>
      val data = FormTool.newGroupForm.bindFromRequest().get
      val userId = Tool.getUserId
      val kindDir = Tool.getUserDataGroupDir(userId)
      val file = new File(kindDir, s"${
        data.fileName
      }.txt")
      val header = if (data.isPaired) {
        List("Sample", "ClassNote", "Pair_ID")
      } else List("Sample", "ClassNote")
      val lines = data.groupNames.zip(data.sampleNames).flatMap {
        case (groupName, sampleNameStr) =>
          val sampleNames = sampleNameStr.mySplit(",")
          sampleNames.zipWithIndex.map {
            case (sampleName, i) =>
              if (data.isPaired) {
                List(s"${
                  sampleName
                }", groupName, s"${
                  i + 1
                }")
              } else List(s"${
                sampleName
              }", groupName)
          }
      }
      (header :: lines).toTxtFile(file)
      val groupMap = file.txtLines.lineMap.map {
        map =>
          (map("sample") -> map("classnote"))
      }.toMap
      val row = GroupFileDataRow(id = 0, fileName = s"${
        file.getName
      }", userId = userId, tags = "other",
        comment = "", uploadTime = new DateTime(), size = file.size.toInt, groupInfo = groupMap)
      groupFileDataDao.insert(row).map {
        x =>
          Ok(ujson.Obj("valid" -> true))
      }
  }

}
